﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ParsecParser.Helpers;
using ParsecParser.Data;
using ParsecParser.Display;
using ParsecParser;

namespace ParsecClient.Classes
{
    public enum TimerTrigger
    {
        YouActivateAnAbility = 1,
        YouGainAnEffect = 2,
        YouLoseAnEffect = 3,
        EnemyGainsAnEffect = 4,
        EnemyLosesAnEffect = 5,
        CombatStarts = 6,
        YouDealDamage = 7,
        YouHeal = 8,
        YouReceiveDamage = 9,
        YouReceiveHealing = 10,
        ATimerExpires = 11,
        EventOccurs = 12,
        Custom = 0,
    }
    public enum TimerTriggerType
    {
        None = 0,
        Custom = 1,
        AbilityActivate = 2,
        EffectOn = 3,
        EffectOff = 4,
        CombatStart = 5,
        Damage = 6,
        Heal = 7,
        TimerExpires = 8,
        EventOccurs = 9
    }
    public enum TimerTriggerTarget
    {
        None = 0,
        Source = 1,
        Target = 2,
        Both = 3,
        Either = 4,
    }
    public enum TimerTriggerTargetName
    {
        None = 0,
        Self = 1,
        NotSelf = 2,
        Custom = 3,
    }
    public enum TimerSound
    {
        None = 0,
        ItsATrap = 1,
        YodaLaugh = 2,
        ChewbaccaArrrrg = 3,
        ChewbaccaLaugh = 4,
        DontGetCocky = 5,
        Utini = 6,
        ThatsNoMoon = 7,
        UseTheForce = 8,
        SmoothLando = 9,
        ArtooScream = 10,
        ArtooWarning = 11,
        PickUpYourVisualScanning = 12,
        VadersBreath = 13,
        IHaveYouNow = 14,
        Impressive = 15,
        ThatGotHim = 16,
        DingAirline = 17,
        DingCashMoney = 18,
        DingCorrectAnswer = 19,
        DingShiny = 20,
        WarningBuzzer = 21,        
        TextToSpeech = 98,
        Custom = 99
    }
    public enum MultipleTimerMode
    {
        AllowMultipleTimers = 0,
        IgnoreRepeatTimers = 1,
        UpdateTimerOnRepeat = 2
    }
    [Serializable]
    public class TimerDefinition
    {
        public static readonly Dictionary<int, string> TimerTrigerValues = new Dictionary<int, string>(){
            {1,"You Activate an Ability"},
            {2,"You Gain an Effect"},
            {3,"You Lose an Effect"},
            {4,"Target Gains an Effect"},
            {5,"Target Loses an Effect"},
            {6,"Combat Starts"},
            {7,"You Deal Damage"},
            {8,"You Heal"},
            {9,"You Take Damage"},
            {10,"You Receive Healing"},
            {11,"A Timer Expires"},
            {12,"An Event Occurs"},
            {0,"Custom"}
        };
        public static readonly Dictionary<int, string> TimerTrigerTypeValues = new Dictionary<int, string>(){
            {0,"None"},
            {1,"Custom"},
            {2,"Ability Activate"},
            {3,"Effect On"},
            {4,"Effect Off"},
            {5,"Combat Start"},
            {6,"Damage"},
            {7,"Heal"},
            {8,"Timer Expires"},
            {9,"Event Occurs"}
        };
        public static readonly Dictionary<int, string> TimerTriggerTargetValues = new Dictionary<int, string>(){
            {0,"None"},
            {1,"Source"},
            {2,"Target"},
            {3,"Both"},
            {4,"Either"}
        };
        public static readonly Dictionary<int, string> TimerTriggerTargetNameValues = new Dictionary<int, string>(){
            {0,"Any"},
            {1,"Self"},
            {2,"NotSelf"},
            {3,"Custom"}
        };
        public static readonly Dictionary<int, string> MultipleTimerModeValues = new Dictionary<int, string>(){
            {0,"Allow Multiple Timers"},
            {1,"Ignore Repeat Timers"},
            {2,"Update Timer On Repeat"}
        };
        public bool IsEnabled { get; set; }
        public string Description { get; set; }
        public TimerTrigger Trigger { get; set; }
        public TimerTriggerType TriggerType { get; set; }
        public string CustomTriggerType { get; set; }
        public TimerTriggerTarget TriggerTarget { get; set; }
        public TimerTriggerTargetName TriggerTargetName { get; set; }
        public string CustomTriggerTargetName { get; set; }
        public string CustomAbilityOrEffect { get; set; }
        public double TimerInterval { get; set; }
        public int RepeatCount { get; set; }
        public TimerSound SoundEffect { get; set; }
        public MultipleTimerMode MultiTimerMode { get; set; }
        public bool IncludeTargetNameInDisplay { get; set; }
        public string CancelTimerName { get; set; }
        public string Category { get; set; }
        public string CustomSoundPath { get; set; }
        public string TextToSpeechText { get; set; }
        public TimerDefinition()
        {
        }
        public TimerDefinition(string description, TimerTrigger trigger, string customAbilityOrEffect, double interval, int repeat, TimerSound soundEffect, MultipleTimerMode multiTimerMode)
        {
            TimerDefinition.SetTimerDefinitionBasedOnTrigger(this, trigger);
            CustomAbilityOrEffect = customAbilityOrEffect;
            TimerInterval = interval;
            RepeatCount = repeat;
            SoundEffect = soundEffect;
            Description = description;
            MultiTimerMode = multiTimerMode;
        }

        public TimerDefinition(string description, TimerTriggerType triggerType, string customType, TimerTriggerTarget triggerTarget, TimerTriggerTargetName triggerName, string customTargetName, string customAbilityOrEffect, double interval, int repeat, TimerSound soundEffect, MultipleTimerMode multiTimerMode)
        {
            Trigger = TimerTrigger.Custom;
            TriggerType = triggerType;
            TriggerTarget = triggerTarget;
            TriggerTargetName = triggerName;
            CustomTriggerType = customType;
            CustomTriggerTargetName = customTargetName;
            CustomAbilityOrEffect = customAbilityOrEffect;
            TimerInterval = interval;
            RepeatCount = repeat;
            SoundEffect = soundEffect;
            Description = description;
            MultiTimerMode = multiTimerMode;
        }
        public void SetTimerDefinitionBasedOnTrigger(TimerTrigger t)
        {
            TimerDefinition.SetTimerDefinitionBasedOnTrigger(this, t);
        }
        public static void SetTimerDefinitionBasedOnTrigger(TimerDefinition td, TimerTrigger t)
        {
            td.Trigger = t;
            switch (td.Trigger)
            {
                case TimerTrigger.CombatStarts:
                    {
                        td.TriggerType = TimerTriggerType.CombatStart;
                        td.TriggerTarget = TimerTriggerTarget.Source;
                        td.TriggerTargetName = TimerTriggerTargetName.Self;
                        td.CustomTriggerType = null;
                        td.CustomTriggerTargetName = null;
                        break;
                    }
                case TimerTrigger.EnemyGainsAnEffect:
                    {
                        td.TriggerType = TimerTriggerType.EffectOn;
                        td.TriggerTarget = TimerTriggerTarget.Target;
                        td.TriggerTargetName = TimerTriggerTargetName.NotSelf;
                        td.CustomTriggerType = null;
                        td.CustomTriggerTargetName = null;
                        break;
                    }
                case TimerTrigger.EnemyLosesAnEffect:
                    {
                        td.TriggerType = TimerTriggerType.EffectOff;
                        td.TriggerTarget = TimerTriggerTarget.Target;
                        td.TriggerTargetName = TimerTriggerTargetName.NotSelf;
                        td.CustomTriggerType = null;
                        td.CustomTriggerTargetName = null;
                        break;
                    }
                case TimerTrigger.YouActivateAnAbility:
                    {
                        td.TriggerType = TimerTriggerType.AbilityActivate;
                        td.TriggerTarget = TimerTriggerTarget.Source;
                        td.TriggerTargetName = TimerTriggerTargetName.Self;
                        td.CustomTriggerType = null;
                        td.CustomTriggerTargetName = null;
                        break;
                    }
                case TimerTrigger.YouDealDamage:
                    {
                        td.TriggerType = TimerTriggerType.Damage;
                        td.TriggerTarget = TimerTriggerTarget.Source;
                        td.TriggerTargetName = TimerTriggerTargetName.Self;
                        td.CustomTriggerType = null;
                        td.CustomTriggerTargetName = null;
                        break;
                    }
                case TimerTrigger.YouGainAnEffect:
                    {
                        td.TriggerType = TimerTriggerType.EffectOn;
                        td.TriggerTarget = TimerTriggerTarget.Target;
                        td.TriggerTargetName = TimerTriggerTargetName.Self;
                        td.CustomTriggerType = null;
                        td.CustomTriggerTargetName = null;
                        break;
                    }
                case TimerTrigger.YouHeal:
                    {
                        td.TriggerType = TimerTriggerType.Heal;
                        td.TriggerTarget = TimerTriggerTarget.Source;
                        td.TriggerTargetName = TimerTriggerTargetName.Self;
                        td.CustomTriggerType = null;
                        td.CustomTriggerTargetName = null;
                        break;
                    }
                case TimerTrigger.YouLoseAnEffect:
                    {
                        td.TriggerType = TimerTriggerType.EffectOff;
                        td.TriggerTarget = TimerTriggerTarget.Target;
                        td.TriggerTargetName = TimerTriggerTargetName.Self;
                        td.CustomTriggerType = null;
                        td.CustomTriggerTargetName = null;
                        break;
                    }
                case TimerTrigger.YouReceiveDamage:
                    {
                        td.TriggerType = TimerTriggerType.Damage;
                        td.TriggerTarget = TimerTriggerTarget.Target;
                        td.TriggerTargetName = TimerTriggerTargetName.Self;
                        td.CustomTriggerType = null;
                        td.CustomTriggerTargetName = null;
                        break;
                    }
                case TimerTrigger.YouReceiveHealing:
                    {
                        td.TriggerType = TimerTriggerType.Heal;
                        td.TriggerTarget = TimerTriggerTarget.Target;
                        td.TriggerTargetName = TimerTriggerTargetName.Self;
                        td.CustomTriggerType = null;
                        td.CustomTriggerTargetName = null;
                        break;
                    }
                case TimerTrigger.ATimerExpires:
                    {
                        td.TriggerType = TimerTriggerType.TimerExpires;
                        td.TriggerTarget = TimerTriggerTarget.None;
                        td.TriggerTargetName = TimerTriggerTargetName.None;
                        td.CustomTriggerType = null;
                        td.CustomTriggerTargetName = null;
                        break;
                    }
                case TimerTrigger.EventOccurs:
                    {
                        td.TriggerType = TimerTriggerType.EventOccurs;
                        td.TriggerTarget = TimerTriggerTarget.None;
                        td.TriggerTargetName = TimerTriggerTargetName.None;
                        td.CustomTriggerType = null;
                        td.CustomTriggerTargetName = null;
                        break;
                    }
                case TimerTrigger.Custom:
                    {
                        throw new ArgumentException("Custom Timers are not supported with this constructor");
                    }
            }
        }
        public ActiveTimer GetActiveTimer(DateTime startTime, DateTime detectedTime, CombatLogParticipant target)
        {
            string triggerId = this.Description;
            string description = this.Description;
            return new ActiveTimer()
            {
                DetectedTime = detectedTime,
                StartTime = TimerInterval == 0 ? detectedTime : startTime,
                EndTime = TimerInterval == 0 ? detectedTime : startTime.AddSeconds(TimerInterval),
                TriggerId = triggerId,
                DisplayName = description,
                SoundEffect = SoundEffect,
                MultiTimerMode = this.MultiTimerMode,
                RepeatCount = this.RepeatCount,
                CancelTimerName = CancelTimerName,
                CustomSoundPath = CustomSoundPath,
                TextToSpeechText = TextToSpeechText,
                Target = target
            };
        }
        public bool TestTimerDefinition(CombatLogEvent ev)
        {
            switch (TriggerType)
            {
                case TimerTriggerType.AbilityActivate:
                    if (!ev.IsAbilityActivate())
                        return false;
                    else
                    {
                        if (string.IsNullOrEmpty(CustomAbilityOrEffect))
                            return false;
                        long aid;
                        if (long.TryParse(CustomAbilityOrEffect, out aid) && ev.Ability.EntityId == aid)
                            break;
                        else if (string.Compare(ev.Ability.DisplayName, CustomAbilityOrEffect, true) != 0)
                            return false;
                        else
                            break;
                    }
                case TimerTriggerType.Damage:
                    if (!ev.IsDamage())
                        return false;
                    else
                    {
                        if (string.IsNullOrEmpty(CustomAbilityOrEffect))
                            return false;
                        long aid;
                        if (long.TryParse(CustomAbilityOrEffect, out aid) && ev.Ability.EntityId == aid)
                            break;
                        else if (string.Compare(ev.Ability.DisplayName, CustomAbilityOrEffect, true) != 0)
                            return false;
                        else
                            break;
                    }
                case TimerTriggerType.Heal:
                    if (!ev.IsHeal())
                        return false;
                    else
                    {
                        if (string.IsNullOrEmpty(CustomAbilityOrEffect))
                            return false;
                        long aid;
                        if (long.TryParse(CustomAbilityOrEffect, out aid) && ev.Ability.EntityId == aid)
                            break;
                        else if (string.Compare(ev.Ability.DisplayName, CustomAbilityOrEffect, true) != 0)
                            return false;
                        else
                            break;
                    }
                case TimerTriggerType.EffectOn:
                    if (!ev.IsEffectOn())
                        return false;
                    else
                    {
                        if (string.IsNullOrEmpty(CustomAbilityOrEffect))
                            return false;
                        long aid;
                        if (long.TryParse(CustomAbilityOrEffect, out aid) && ev.EffectName.EntityId == aid)
                            break;
                        else if (string.Compare(ev.EffectName.DisplayName, CustomAbilityOrEffect, true) == 0)
                            break;
                        else
                            return false;
                    }
                case TimerTriggerType.EffectOff:
                    if (!ev.IsEffectOff())
                        return false;
                    else
                    {
                        if (string.IsNullOrEmpty(CustomAbilityOrEffect))
                            return false;
                        long aid;
                        if (long.TryParse(CustomAbilityOrEffect, out aid) && ev.EffectName.EntityId == aid)
                            break;
                        else if (string.Compare(ev.EffectName.DisplayName, CustomAbilityOrEffect, true) == 0)
                            break;
                        else
                            return false;
                    }
                case TimerTriggerType.EventOccurs:
                    if (!ev.IsEvent())
                        return false;
                    else
                    {
                        if (string.IsNullOrEmpty(CustomAbilityOrEffect))
                            return false;
                        long aid;
                        if (long.TryParse(CustomAbilityOrEffect, out aid) && ev.EffectName.EntityId == aid)
                            break;
                        else if (string.Compare(ev.EffectName.DisplayName, CustomAbilityOrEffect, true) == 0)
                            break;
                        else
                            return false;
                    }
                case TimerTriggerType.CombatStart:
                    if (ev.EffectName != null && ev.EffectName.EntityId != CombatLogConstants.EffectNames.EnterCombat)
                        return false;
                    else
                        break;
                case TimerTriggerType.TimerExpires:
                    return false;
                case TimerTriggerType.Custom:
                    if (string.IsNullOrEmpty(CustomTriggerType))
                        return false;
                    else if (ev.EffectType != null && string.Compare(ev.EffectType.DisplayName, CustomTriggerType, true) == 0)
                    {
                        if (string.IsNullOrEmpty(CustomAbilityOrEffect))
                            return false;
                        long aid;
                        if (long.TryParse(CustomAbilityOrEffect, out aid) && (ev.EffectName.EntityId == aid || ev.Ability.EntityId == aid))
                            break;
                        else if ((ev.EffectName != null && string.Compare(ev.EffectName.DisplayName, CustomAbilityOrEffect, true) == 0) || (ev.Ability != null && string.Compare(ev.Ability.DisplayName, CustomAbilityOrEffect, true) == 0))
                            break;
                        else
                            return false;
                    }
                    else
                        return false;
            }
            switch (TriggerTargetName)
            {
                case TimerTriggerTargetName.Self:
                {
                    switch (TriggerTarget)
                    {
                        case TimerTriggerTarget.Source:
                        {
                            if (!ev.IsSource())
                                return false;
                            else
                                break;
                        }
                        case TimerTriggerTarget.Target:
                        {
                            if (!ev.IsTarget())
                                return false;
                            else
                                break;
                        }
                        case TimerTriggerTarget.None:
                        {
                            if (ev.IsTarget() || ev.IsSource())
                                return false;
                            else
                                break;
                        }
                        case TimerTriggerTarget.Either:
                        {
                            if (!ev.IsTarget() && !ev.IsSource())
                                return false;
                            else
                                break;
                        }
                        case TimerTriggerTarget.Both:
                        {
                            if (!ev.IsTarget() || !ev.IsSource())
                                return false;
                            else
                                break;
                        }
                    }
                    break;
                }
                case TimerTriggerTargetName.NotSelf:
                {
                    switch (TriggerTarget)
                    {
                        case TimerTriggerTarget.Source:
                            {
                                if (ev.IsSource())
                                    return false;
                                else
                                    break;
                            }
                        case TimerTriggerTarget.Target:
                            {
                                if (ev.IsTarget())
                                    return false;
                                else
                                    break;
                            }
                        case TimerTriggerTarget.None:
                            {
                                if (!ev.IsTarget() && !ev.IsSource())
                                    return false;
                                else
                                    break;
                            }
                        case TimerTriggerTarget.Either:
                            {
                                if (ev.IsTarget() || ev.IsSource())
                                    return false;
                                else
                                    break;
                            }
                        case TimerTriggerTarget.Both:
                            {
                                if (ev.IsTarget() && ev.IsSource())
                                    return false;
                                else
                                    break;
                            }
                    }
                    break;
                }
                case TimerTriggerTargetName.Custom:
                {
                    if (string.IsNullOrEmpty(CustomTriggerTargetName))
                        return false;
                    switch (TriggerTarget)
                    {
                        case TimerTriggerTarget.Source:
                            {
                                long aid;
                                if (long.TryParse(CustomTriggerTargetName, out aid) && ev.Source.EntityId == aid)
                                    break;
                                else if (string.Compare(ev.Source.DisplayName, CustomTriggerTargetName, true) != 0)
                                    return false;
                                else
                                    break;
                            }
                        case TimerTriggerTarget.Target:
                            {
                                long aid;
                                if (long.TryParse(CustomTriggerTargetName, out aid) && ev.Target.EntityId == aid)
                                    break;
                                else if (string.Compare(ev.Target.DisplayName, CustomTriggerTargetName, true) != 0)
                                    return false;
                                else
                                    break;
                            }
                        case TimerTriggerTarget.None:
                            {
                                long aid;
                                if (long.TryParse(CustomTriggerTargetName, out aid) && (ev.Source.EntityId != aid && ev.Target.EntityId != aid))
                                    break;
                                else if (string.Compare(ev.Target.DisplayName, CustomTriggerTargetName, true) == 0 || string.Compare(ev.Source.DisplayName, CustomTriggerTargetName, true) == 0)
                                    return false;
                                else
                                    break;
                            }
                        case TimerTriggerTarget.Either:
                            {
                                long aid;
                                if (long.TryParse(CustomTriggerTargetName, out aid) && (ev.Source.EntityId == aid || ev.Target.EntityId == aid))
                                    break;
                                if (string.Compare(ev.Target.DisplayName, CustomTriggerTargetName, true) != 0 && string.Compare(ev.Source.DisplayName, CustomTriggerTargetName, true) != 0)
                                    return false;
                                else
                                    break;
                            }
                        case TimerTriggerTarget.Both:
                            {
                                long aid;
                                if (long.TryParse(CustomTriggerTargetName, out aid) && (ev.Source.EntityId == aid && ev.Target.EntityId == aid))
                                    break;
                                if (string.Compare(ev.Target.DisplayName, CustomTriggerTargetName, true) != 0 || string.Compare(ev.Source.DisplayName, CustomTriggerTargetName, true) != 0)
                                    return false;
                                else
                                    break;
                            }
                    }
                    break;
                }
            }
            return true;
        }
        public static List<TimerDefinition> GetDefaults()
        {
            return new List<TimerDefinition>() {
            new TimerDefinition("One Minute Into Fight", TimerTrigger.CombatStarts, null, 60, 0, TimerSound.IHaveYouNow, MultipleTimerMode.IgnoreRepeatTimers) ,
            new TimerDefinition("Vital Shot Timer", TimerTrigger.YouActivateAnAbility, "Vital Shot", 15, 0, TimerSound.Utini, MultipleTimerMode.AllowMultipleTimers) ,
            new TimerDefinition("Kinetic Ward Timer", TimerTrigger.YouActivateAnAbility, "Kinetic Ward", 13, 0, TimerSound.ArtooWarning, MultipleTimerMode.UpdateTimerOnRepeat) ,
            new TimerDefinition("Shadow Protection Lost", TimerTrigger.YouLoseAnEffect, "Shadow Protection", 0, 0, TimerSound.DontGetCocky, MultipleTimerMode.AllowMultipleTimers) ,
            new TimerDefinition("Power Boost Timer", TimerTrigger.YouGainAnEffect, "Power Boost", 120, 0, TimerSound.YodaLaugh, MultipleTimerMode.AllowMultipleTimers) };
        }
    }
    [Serializable]
    public class ActiveTimer
    {
        public DateTime DetectedTime { get; set; }
        public DateTime StartTime { get; set; }
        public DateTime EndTime { get; set; }
        public MultipleTimerMode MultiTimerMode { get; set; }
        public TimerSound SoundEffect { get; set; }
        public string TriggerId { get; set; }
        public string DisplayName { get; set; }
        public int RepeatCount { get; set; }
        public CombatLogParticipant Target { get; set; }
        public string CancelTimerName { get; set; }
        public string CustomSoundPath { get; set; }
        public string TextToSpeechText { get; set; }
        public double GetPercentageRemaining(DateTime currentTime)
        {
            double remainingMS = currentTime.Subtract(StartTime).TotalMilliseconds;
            double totalMS = EndTime.Subtract(StartTime).TotalMilliseconds;
            if (double.IsNaN(totalMS) || double.IsNaN(remainingMS) ||
                double.IsInfinity(totalMS) || double.IsInfinity(remainingMS) ||
                double.IsNegativeInfinity(totalMS) || double.IsNegativeInfinity(remainingMS) ||
                double.IsPositiveInfinity(totalMS) || double.IsPositiveInfinity(remainingMS))
                return 0;
            if (remainingMS <= 0)
                return 0;
            if (totalMS <= 0)
                return 0;
            return (remainingMS / totalMS) * 100;
        }
    }
}
